package com.torchclx;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.locks.LockSupport;


/*
    定时任务实现思路
    1、要有一个东西(触发器)能把提交的任务，按照延迟时间放入到线程池里面，而且不能只提交一次，执行完本次之后，要加上延迟时间重新提交
    2、任务执行的时间和当前时间判断，如果小于当前时间，那要立刻放入线程池执行，如果大于当前时间，就不用管，等待之间到了就执行
    3、要有一个容器，存放任务，任务需要按照执行顺序，从早到晚排序，考虑优先级阻塞队列
    4、如果容器里面第一个要执行的任务的执行时间在100ms之后，且已经在阻塞等待了，这个时候添加两个50ms的任务，需要唤醒等待，且要从头开始拿任务来执行
 */
public class ScheduledService {

    ExecutorService executorService = Executors.newFixedThreadPool(10);
    Trigger trigger = new Trigger();


    public void schedule(Runnable task, long delay) {
        Job job = new Job();
        job.setTask(task);
        job.setStartTime(System.currentTimeMillis()+delay);
        job.setDelayTime(delay);
        trigger.queue.offer(job);
        trigger.wakeUp();

    }


    class Trigger{

        PriorityBlockingQueue<Job> queue = new PriorityBlockingQueue<>();

        private final Thread thread = new Thread(()->{
            while (true) {
                while (queue.isEmpty()) {
                    LockSupport.park();
                }
                Job curJob = queue.peek();
                if (curJob.isInterrupted()) {
                    break;
                }

                long startTime = curJob.getStartTime();
                if(startTime<= System.currentTimeMillis()) {
                    curJob = queue.poll();
                    executorService.execute(curJob.getTask());

                    curJob.setStartTime(System.currentTimeMillis()+curJob.getDelayTime());
                    queue.offer(curJob);
                }else {
                    LockSupport.parkUntil(curJob.getStartTime());
                }

            }

        });


        {
            thread.start();
        }

        public void wakeUp () {
            LockSupport.unpark(thread);
        }


        public PriorityBlockingQueue<Job> getQueue() {
            return queue;
        }

        public void setQueue(PriorityBlockingQueue<Job> queue) {
            this.queue = queue;
        }
    }

}


